/*
	The Raja Toolkit.
	Copyright (C) 2011 Raja-Toolkit Foundation

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 3.0 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#ifndef RAJA_MATH_MATRIX
#define RAJA_MATH_MATRIX

#include "../types.hpp"
#include "vector.hpp"


/// @namespace raja
namespace raja
{

/// @namespace math
namespace math
{
/// @defgroup math math
/// @{

///
/// mat2 structure
/// The matrix is layed out as the following:
/// M	= | m00 m01 | = | col[0].x col[1].x |
///       | m10 m11 |   | col[0].y col[1].y |
///
template<typename T>
struct tmat2
{
	typedef T					atom_type;
	typedef tvec2<T>			vec2_t;

	vec2_t						col[2];

	tmat2();

	tmat2(T m00, T m10, T m01, T m11);
	tmat2(const vec2_t &c0, const vec2_t &c1);
	tmat2(const tmat2 &m);

	/// access
	inline vec2_t& operator[](int i)
	{
		return col[i];
	}
	inline vec2_t operator[](int i) const
	{
		return col[i];
	}

	/// addition
	tmat2& operator +=(const tmat2 &m);

	/// Subtraction
	tmat2& operator -=(const tmat2 &m);

	/// multiplication
	tmat2& operator *=(const tmat2 &m);

	/// division
	tmat2& operator /=(const tmat2 &m);

	/// prefix increment
	tmat2& operator++();

	/// prefix decrement
	tmat2& operator--();

	/// postfix increment
	tmat2 operator++(int);

	/// postfix decrement
	tmat2 operator--(int);

	/// assignment
	tmat2& operator =(const tmat2 &m);
}; // mat2

///
/// mat3 structure
/// The matrix is layed out as the following:
///		  | m00 m01 m02 |   | col[0].x col[1].x col[2].x |
///	M	= | m10 m11 m12 | = | col[0].y col[1].y col[2].y |
///		  | m20 m21 m22 |   | col[0].z col[1].z col[2].z |
///
template<typename T>
struct tmat3
{
	typedef T					atom_type;
	typedef tvec3<T>			vec3_t;

	vec3_t						col[3];

	tmat3();
	tmat3(T m00, T m10, T m20,
		  T m01, T m11, T m21,
		  T m02, T m12, T m22);
	tmat3(const vec3_t &c0, const vec3_t &c1, const vec3_t &c2);
	tmat3(const tmat3 &m);

	/// access
	inline vec3_t& operator[](int i)
	{
		return col[i];
	}
	inline vec3_t operator[](int i) const
	{
		return col[i];
	}

	/// addition
	tmat3& operator +=(const tmat3 &m);

	/// substraction
	tmat3& operator -=(const tmat3 &m);

	/// multiplication
	tmat3& operator *=(const tmat3 &m);

	/// division
	tmat3& operator /=(const tmat3 &m);

	/// prefix increment
	tmat3& operator++();

	/// prefix decrement
	tmat3& operator--();

	/// postfix increment
	tmat3 operator++(int);

	/// postfix decrement
	tmat3 operator--(int);

	/// assignment
	tmat3& operator =(const tmat3 &m);
}; // mat3

///
/// mat4 structure
/// The matrix is layed out as the following:
///       | m00 m01 m02 m03 |   | col[0].x col[1].x col[2].x col[3].x |
///	M	= | m10 m11 m12 m13 | = | col[0].y col[1].y col[2].y col[3].y |
///		  | m20 m21 m22 m23 |   | col[0].z col[1].z col[2].z col[3].z |
///		  | m30 m31 m32 m33 |   | col[0].w col[1].w col[2].w col[3].w |
///
template<typename T>
struct tmat4
{
	typedef T					atom_type;
	typedef tvec4<T>			vec4_t;

	vec4_t						col[4];

	tmat4();
	tmat4(T m00, T m10, T m20, T m30,
		  T m01, T m11, T m21, T m31,
		  T m02, T m12, T m22, T m32,
		  T m03, T m13, T m23, T m33);
	tmat4(const vec4_t &c0, const vec4_t &c1, const vec4_t &c2, const vec4_t &c3);
	tmat4(const tmat4 &m);

	/// access
	inline vec4_t& operator[](int i)
	{
		return col[i];
	}
	inline vec4_t operator[](int i) const
	{
		return col[i];
	}

	/// addition
	tmat4& operator +=(const tmat4 &m);

	/// substraction
	tmat4& operator -=(const tmat4 &m);

	/// multiplication
	tmat4& operator *=(const tmat4 &m);

	/// division
	tmat4& operator /=(const tmat4 &m);

	/// prefix increment
	tmat4& operator++();

	/// prefix decrement
	tmat4& operator--();

	/// postfix increment
	tmat4 operator++(int);

	/// postfix decrement
	tmat4 operator--(int);

	/// assignment
	tmat4& operator =(const tmat4 &v);
}; // mat4

/// @name comparison
/// comparison operators
/// @{
template<typename T>
bool operator == (const tmat2<T> &m0, const tmat2<T> &m1);

template<typename T>
bool operator == (const tmat3<T> &m0, const tmat3<T> &m1);

template<typename T>
bool operator == (const tmat4<T> &m0, const tmat4<T> &m1);

template<typename T>
inline bool operator != (const tmat2<T> &m0, const tmat2<T> &m1)
{
	return !(m0 == m1);
}

template<typename T>
inline bool operator != (const tmat3<T> &m0, const tmat3<T> &m1)
{
	return !(m0 == m1);
}

template<typename T>
inline bool operator != (const tmat4<T> &m0, const tmat4<T> &m1)
{
	return !(m0 == m1);
}
/// @}

/// @name addition
/// addition operators
/// @{
template<typename T>
tmat2<T> operator + (const tmat2<T> &m, T s);

template<typename T>
tmat3<T> operator + (const tmat3<T> &m, T s);

template<typename T>
tmat4<T> operator + (const tmat4<T> &m, T s);

/////

template<typename T>
tmat2<T> operator + (T s, const tmat2<T> &m);

template<typename T>
tmat3<T> operator + (T s, const tmat3<T> &m);

template<typename T>
tmat4<T> operator + (T s, const tmat4<T> &m);

/////

template<typename T>
tmat2<T> operator + (const tmat2<T> &m0, const tmat2<T> &m1);

template<typename T>
tmat3<T> operator + (const tmat3<T> &m0, const tmat3<T> &m1);

template<typename T>
tmat4<T> operator + (const tmat4<T> &m0, const tmat4<T> &m1);
/// @}

///@name substraction
/// substraction operators
/// @{
template<typename T>
tmat2<T> operator - (const tmat2<T> &m, T s);

template<typename T>
tmat3<T> operator - (const tmat3<T> &m, T s);

template<typename T>
tmat4<T> operator - (const tmat4<T> &m, T s);

/////

template<typename T>
tmat2<T> operator - (T s, const tmat2<T> &m);

template<typename T>
tmat3<T> operator - (T s, const tmat3<T> &m);

template<typename T>
tmat4<T> operator - (T s, const tmat4<T> &m);

/////

template<typename T>
tmat2<T> operator -(const tmat2<T> &m0, const tmat2<T> &m1);

template<typename T>
tmat3<T> operator -(const tmat3<T> &m0, const tmat3<T> &m1);

template<typename T>
tmat4<T> operator -(const tmat4<T> &m0, const tmat4<T> &m1);
/// @}

/// @name division
/// division operators
/// @{
template<typename T>
tmat2<T> operator / (const tmat2<T> &m, T s);

template<typename T>
tmat3<T> operator / (const tmat3<T> &m, T s);

template<typename T>
tmat4<T> operator / (const tmat4<T> &m, T s);

/////

template<typename T>
tmat2<T> operator / (T s, const tmat2<T> &m);

template<typename T>
tmat3<T> operator / (T s, const tmat3<T> &m);

template<typename T>
tmat4<T> operator / (T s, const tmat4<T> &m);

/////

template<typename T>
tmat2<T> operator / (const tmat2<T> &m0, const tmat2<T> &m1);

template<typename T>
tmat3<T> operator / (const tmat3<T> &m0, const tmat3<T> &m1);

template<typename T>
tmat4<T> operator / (const tmat4<T> &m0, const tmat4<T> &m1);
/// @}

/// @name multiplication
/// multiplication operators
/// @{
template<typename T>
tmat2<T> operator * (const tmat2<T> &m, T s);

template<typename T>
tmat3<T> operator * (const tmat3<T> &m, T s);

template<typename T>
tmat4<T> operator * (const tmat4<T> &m, T s);

/////

template<typename T>
tmat2<T> operator * (T s, const tmat2<T> &m);

template<typename T>
tmat3<T> operator * (T s, const tmat3<T> &m);

template<typename T>
tmat4<T> operator * (T s, const tmat4<T> &m);

/////

template<typename T>
tmat2<T> operator * (const tmat2<T> &m0, const tmat2<T> &m1);

template<typename T>
tmat3<T> operator * (const tmat3<T> &m0, const tmat3<T> &m1);

template<typename T>
tmat4<T> operator * (const tmat4<T> &m0, const tmat4<T> &m1);
/// @}

/// @name vector matrix multiplications
/// @{
template<typename T>
tvec2<T> operator * (const tvec2<T> &v, const tmat2<T> &m);

template<typename T>
tvec2<T> operator * (const tmat2<T> &m, const tvec2<T> &v);


template<typename T>
tvec3<T> operator * (const tvec3<T> &v, const tmat3<T> &m);

template<typename T>
tvec3<T> operator * (const tmat3<T> &m, const tvec3<T> &v);


template<typename T>
tvec4<T> operator * (const tvec4<T> &v, const tmat4<T> &m);

template<typename T>
tvec4<T> operator * (const tmat4<T> &m, const tvec4<T> &v);
/// @}

/// @name matrix determinant
/// @{
template<typename T>
T determinant(const tmat2<T> &m);

template<typename T>
T determinant(const tmat3<T> &m);

template<typename T>
T determinant(const tmat4<T> &m);
/// @}

/// @name matrix transpose
/// @{
template<typename T>
tmat2<T> transpose(const tmat2<T> &m);

template<typename T>
tmat3<T> transpose(const tmat3<T> &m);

template<typename T>
tmat4<T> transpose(const tmat4<T> &m);
/// @}

/// @name matrix inverse
/// @{
template<typename T>
tmat2<T> inverse(const tmat2<T> &m);

template<typename T>
tmat3<T> inverse(const tmat3<T> &m);

template<typename T>
tmat4<T> inverse(const tmat4<T> &m);
/// @}

///////////////////////////////////////////////////////////////////////////////
typedef tmat2<real32>		mat2;
typedef tmat2<real64>		dmat2;

typedef tmat3<real32>		mat3;
typedef tmat3<real64>		dmat3;

typedef tmat4<real32>		mat4;
typedef tmat4<real64>		dmat4;
///////////////////////////////////////////////////////////////////////////////

/// @}
}// namespace math
/// @}
} // namespace raja

#endif // RAJA_MATH_MATRIX
